package fr.irit.etracetool.query;

import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.uml2.uml.NamedElement;

import fr.irit.etracetool.etrace.EObjectOrigin;
import fr.irit.etracetool.etrace.ETrace;
import fr.irit.etracetool.etrace.Link;

public class Predicate {

	protected Resource source;
	protected Resource target;
	protected ETrace trace;
	public Collection<EObject> sourceContents;
	public Collection<EObject> targetContents;

	public Predicate(Resource source, Resource target, ETrace trace) {
		super();
		this.source = source;
		this.target = target;
		this.trace = trace;
		sourceContents = new HashSet<EObject>();
		targetContents = new HashSet<EObject>();
		for (Iterator<EObject> i = source.getAllContents(); i.hasNext(); ) {
			sourceContents.add(i.next());
		}
		for (Iterator<EObject> i = target.getAllContents(); i.hasNext(); ) {
			targetContents.add(i.next());
		}
	}

	public Resource getSource() {
		return source;
	}

	public Resource getTarget() {
		return target;
	}

	public ETrace getTrace() {
		return this.trace;
	}

	/**
	 * @return true if the list contains the EObject eo, with a real equality.
	 * @see EcoreUtil.EqualityHelper
	 **/
	public boolean contains(EObject eo, Collection<EObject> list) {
		for (EObject e: list) {
			if (EcoreUtil.equals(eo, e)) {
				return true;
			} 
		}
		return false;
	}

	/**
	 * Test if the eo EObject are in the source model.
	 * @param eo : the object to be tested
	 * @return true if contains(eo, soruceContents)
	 */
	public boolean sourcePredicate(EObject eo) {
		//if (eo.eIsProxy()) System.err.println("Proxy !");
		if (eo.eResource() != null && eo.eResource().getURI().fileExtension().equals("uml")) 
			return true;
		return false;
		//return contains(eo, this.sourceContents);
	}

	/**
	 * Test if the eo EObject are in the target model.
	 * @param eo : the object to be tested
	 * @return true if contains(eo, targetContents)
	 */
	public boolean targetPredicate(EObject eo) {
		if (eo.eResource() != null && eo.eResource().getURI().fileExtension().equals("astjava")) 
			return true;
		return false;
		//return contains(eo, this.targetContents);
	}

	/**
	 * Test if the eo EObject are not in the source model and not in the target model.
	 * @param eo : the object to be tested
	 * @return true if !targetPredicate(eo) && !sourcePredicate(eo)
	 */
	public boolean intermediatePredicate(EObject eo) {
		return !targetPredicate(eo) && !sourcePredicate(eo);
	}

	/**
	 * Return the set of the source model elements referenced in the trace (in the source feature or in the target feature of a link)
	 * @return the set of eo where eo is in trace.getAllReferencedElements() and sourcePredicate(eo) is true.
	 */
	public Set<EObject> AllSource() {
		HashSet<EObject> hs = new HashSet<EObject>();
		for (EObject eo : trace.getAllReferencedElements()) {
			//System.out.print(eo + "->" );
			if (sourcePredicate(eo)) {
				//System.out.println(" OK");
				hs.add(eo);
			} //else System.out.println();
		}
		return hs;
	}

	/**
	 * Return the set of the target model elements referenced in the trace (in the source feature or in the target feature of a link)
	 * @return the set of eo where eo is in trace.getAllReferencedElements() and targetPredicate(eo) is true.
	 */
	public Set<EObject> AllTarget() {
		HashSet<EObject> hs = new HashSet<EObject>();
		for (EObject eo : trace.getAllReferencedElements()) {
			if (targetPredicate(eo)) {
				hs.add(eo);
			}
		}
		return hs;
	}

	/**
	 * Return the set of model elements of the trace (in the source feature or in the target feature of a link) which not referenced in the source nor in the target model.
	 * @return the set of eo where eo is in trace.getAllReferencedElements() and targetPredicate(eo) is true.
	 */
	public Set<EObject> AllIntermediate() {
		HashSet<EObject> hs = new HashSet<EObject>();
		for (EObject eo : trace.getAllReferencedElements()) {
			if (intermediatePredicate(eo)) {
				hs.add(eo);
			}
		}
		return hs;
	}

	public void initializeLinkType() {
		EList<Link> l = trace.getAllLinks();
		for (Link link: l) {
			if (link.getSource()!=null) {
				if (sourcePredicate(link.getSource())) {
					link.getType().setSourceOrigin(EObjectOrigin.SOURCE);
				}
				else if (targetPredicate(link.getSource())) {
					link.getType().setSourceOrigin(EObjectOrigin.TARGET);
				}
				else if (intermediatePredicate(link.getSource())) {
					link.getType().setSourceOrigin(EObjectOrigin.INTERMEDIATE);
				}
			}
			if (link.getTarget()!=null) {
				if (sourcePredicate(link.getTarget())) {
					link.getType().setTargetOrigin(EObjectOrigin.SOURCE);
				}
				else if (targetPredicate(link.getTarget())) {
					link.getType().setTargetOrigin(EObjectOrigin.TARGET);
				}
				else if (intermediatePredicate(link.getTarget())) {
					link.getType().setTargetOrigin(EObjectOrigin.INTERMEDIATE);
				}
			}
		}
	}
}